package com.oj.friend.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.oj.common.core.constants.CacheConstants;
import com.oj.common.core.constants.Constants;
import com.oj.common.core.constants.JwtConstants;
import com.oj.common.core.domain.LoginUser;
import com.oj.common.core.domain.R;
import com.oj.common.core.domain.vo.LoginUserVO;
import com.oj.common.core.enums.ResultCode;
import com.oj.common.core.enums.UserIdentity;
import com.oj.common.core.utils.JwtUtils;
import com.oj.common.core.utils.ThreadLocalUtil;
import com.oj.common.redis.service.RedisService;
import com.oj.common.security.exception.ServiceExpection;
import com.oj.common.security.service.TokenService;
import com.oj.friend.domain.user.User;
import com.oj.friend.domain.user.dto.UserDTO;
import com.oj.friend.domain.user.dto.UserEditDTO;
import com.oj.friend.domain.user.vo.LoginUserInfo;
import com.oj.friend.manager.UserCacheManager;
import com.oj.friend.mapper.UserMapper;
import com.oj.friend.service.IUserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description
 * @Author bin
 * @Date 2025/5/23
 */

@Service
@RefreshScope
public class UserServiceImpl implements IUserService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserMapper userMapper;

    @Resource
    private UserCacheManager userCacheManager;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${sms.isSend:false}")
    private boolean isSend;  //  true  关闭 false

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;


    private static final Long EXP = 5L;

    private static final Long sendLimit = 50L;


    @Override
    public R<Void> sendCode(UserDTO userDTO) {
        if (!checkPhone(userDTO.getPhone())) {
            throw new ServiceExpection(ResultCode.FAILED_USER_PHONE_NOT_VALIDATE);
        }
        // 通过验证码还剩下的有效时间来判断经过时间 从而判断是否还可以请求验证码
        Long expire = redisService.getExpire(phoneCodeKey(userDTO.getPhone()), TimeUnit.MILLISECONDS);
        if (expire != null && EXP * 60 - expire < 60) {
            throw new ServiceExpection(ResultCode.FAILED_USER_FREQUENTLY);
        }
        // 每天的验证码数量限制 一天50次
        String phoneCodeTime = phoneCodeTimes(userDTO);
        Long sendTimes = redisService.getCacheObject(phoneCodeTime, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            //超过次数限制
            throw new ServiceExpection(ResultCode.FAILED_USER_TIMELIMIT);
        }
        // 生成验证码
        String code = isSend ? RandomUtil.randomNumbers(6) : "123456";

        // 验证码Key-value存到redis  5分钟过期时间(EXP时间)
        redisService.setCacheObject(phoneCodeKey(userDTO.getPhone()), code, EXP, TimeUnit.MINUTES);
        // 发送验证码
        System.out.println("验证码：" + code);
        //发送失败的话
        if (false) {
            throw new ServiceExpection(ResultCode.FAILED_USER_SEND_FAILED);
        }
        // 此手机号的发送次数 + 1 会自动创建Key value = 1
        redisService.increment(phoneCodeTime);
        if (sendTimes == null) {
            //第一次登录
            long between = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(phoneCodeTime, between, TimeUnit.SECONDS);
        }
        return R.ok();
    }

    @Override
    public R<String> codeLogin(String phone, String code) {
        //先完成验证码的验证 证明手机号是自己的
        String codeKey = phoneCodeKey(phone);
        String redisCode = redisService.getCacheObject(codeKey, String.class);
        if (redisCode == null) {
            return R.fail(ResultCode.FAILED_INVALID_CODE);
        }
        if (!redisCode.equals(code)) {
            return R.fail(ResultCode.FAILED_INVALID_CODE);
        }
        //验证码
        redisService.deleteObject(codeKey); // 删除掉此redis中的验证码 因为无效了
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (user == null) {
            //注册逻辑
            user = new User();
            user.setPhone(phone);
            user.setNickName("用户_" + RandomUtil.randomString(5));
            user.setStatus(Constants.NORMAL);
            userMapper.insert(user);
        }
        // 说明是老用户
        String token = tokenService.createToken(user.getUserId(), UserIdentity.ORDINARY.getValue(), secret, user.getNickName(), user.getHeadImage());
        return R.ok(token);

    }

    @Override
    public R<Void> logout(String token) {
        String redisTokenKey = tokenService.getRedisTokenKey(token, secret);
        if (redisService.hasKey(redisTokenKey)) {
            if (redisService.deleteObject(redisTokenKey)) {
                return R.ok();
            }
            return R.fail();
        } else {
            return R.fail();
        }
    }

    @Override
    public R<LoginUserVO> info(String token) {
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        loginUserVO.setIdentity(loginUser.getIdentity());
        if(loginUser.getHeadImage() != null){
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }
        return R.ok(loginUserVO);
    }

    @Override
    public R<LoginUserInfo> detail() {
        Long userId = ThreadLocalUtil.get(JwtConstants.LOGIN_USER_ID, Long.class);
        LoginUserInfo loginUserInfo = userCacheManager.getUserById(userId);
        if (loginUserInfo == null) {
            throw new ServiceExpection(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if (loginUserInfo.getHeadImage() != null){
            loginUserInfo.setHeadImage(downloadUrl + loginUserInfo.getHeadImage());
        }
        return R.ok(loginUserInfo);
    }

    @Override
    public int edit(UserEditDTO userEditDTO) {
        User user = getUser();
        BeanUtil.copyProperties(userEditDTO, user);
        //更新一下redis中的缓存
        return refreshUserAll(user);
    }

    @Override
    public int updateHeadImage(String headImageName) {
        User user = getUser();
        user.setHeadImage(headImageName);

        return refreshUserAll(user);
    }

    private int refreshUserAll(User user) {
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),
                ThreadLocalUtil.get(JwtConstants.LOGIN_USER_KEY, String.class)
        );
        return userMapper.updateById(user);
    }

    private User getUser() {
        Long userId = ThreadLocalUtil.get(JwtConstants.LOGIN_USER_ID, Long.class);
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserId, userId));
        return user;
    }

    private static String phoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }

    private static String phoneCodeTimes(UserDTO userDTO) {
        return CacheConstants.CODE_TIME_KEY + userDTO.getPhone();
    }

    public static boolean checkPhone(String phone) {
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        Matcher m = regex.matcher(phone);
        return m.matches();
    }

}
